﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Dtd.Schema;
using Engine.Sgml.Validation.Rules;
using System.Xml;
using System.Diagnostics;
using Engine.Sgml.Configuration;
using Engine.Sgml.Allocation;
using Engine.Sgml.Assembling;
using Engine.Sgml.Matching;
using Engine.Sgml.Merging;

namespace Engine.Sgml.Validation {
    internal sealed class DocumentValidator {
        private AllocationContext allocationContext;
        private ValidationRuleCollection validationRules;

        private ModelMatcher modelMatcher;
        private MatchMerger matchMerger;

        public DocumentValidator(SgmlDocument document, DtdSchema schema) {
            Guard.ArgumentNotNull(document, "document");
            Guard.ArgumentNotNull(schema, "schema");

            validationRules = new ValidationRuleCollection(schema.Names);
            document.ValidatorBehavior.Rules = validationRules;
            allocationContext = new AllocationContext(validationRules);

            modelMatcher = new ModelMatcher();
            matchMerger = new MatchMerger();
            }

        private class ElementComparer : IEqualityComparer<SElement> {
            public bool Equals(SElement x, SElement y) {
                return object.ReferenceEquals(x, y);
                }
            public int GetHashCode(SElement obj) {
                return obj.GetHashCode();
                }
            }

        public ValidationRuleCollection ValidationRules {
            get { return validationRules; }
            }

        public AllocationContext AllocationContext {
            get { return allocationContext; }
            }

        public void Validate(SgmlDocument document) {
            Debug.Assert(document != null);
            allocationContext.Blocks.Clear();
            allocationContext.Schema = document.Schema;
            using (NodeIterator nodeIterator = new NodeIterator(document.Root)) {
                while (nodeIterator.MoveNext()) {
                    SElement node = nodeIterator.Current;
                    if (node.HasDeclaration) {
                        //Debug.Assert(node.LineNumber != 723);
                        IList<Capture> captures = modelMatcher.Match(node);
                        IList<InconsistentBlock> blocks = matchMerger.Merge(captures);
                        if (MatchAssembler.HasUncompletedMatch(captures))
                            allocationContext.Blocks.Add(node, blocks, true);
                        else if (blocks.Count > 0)
                            allocationContext.Blocks.Add(node, blocks);
                        }
                    }
                }

            if (allocationContext.Blocks.Count > 0) {
                IAllocationStrategy strategy = allocationContext.GetStrategy("UncompletedBlocksAllocation");
                strategy.Allocate(allocationContext.Blocks, allocationContext);
                allocationContext.Blocks.RemoveAllocated();
                }
            else
                return;
            if (allocationContext.Blocks.Count > 0) {
                IAllocationStrategy strategy = allocationContext.GetStrategy("BlocksAllocation");
                strategy.Allocate(allocationContext.Blocks, allocationContext);
                allocationContext.Blocks.RemoveAllocated();
                }
            else
                return;
            if (allocationContext.Blocks.Count > 0) {
                IAllocationStrategy strategy = allocationContext.GetStrategy("InnerBlocksAllocation");
                do {
                    strategy.Allocate(allocationContext.Blocks, allocationContext);
                    allocationContext.Blocks.RemoveAllocated();
                    } while (allocationContext.Blocks.Count > 0);
                }
            else
                return;
            }
        }
    }